<?php
if (! defined ( 'IN_BIC' )) {
	die ( '非法Model访问来源!' );
}
/**
 *
 * @package Bic
 * @author Bic
 * @version $Id: BicSqli.php 2010年5月11日 21时32分29秒
 * @copyright (c) 2010 Bic
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 *
 * Minimum Requirement: PHP 5.0.1
 */
abstract class Bic_Model_Abstract extends Bic_Base implements Bic_Model_Interface {
    const columnFragmentIndex = 'key';
    const columnValueIndex = 'val';
    /**
     *数据库驱动适配器
     * @var \Bic_Db_Drivers_Mysqli 
     */
    protected static $_db;
    
    /**
     *当前Model使用的表字段
     * @var type 
     */
    protected $_fields;

    protected static $_config   = array(
        'identifier_quote_character'    => null,
        'cache'                         => false
    );
    
    /**
     *当前模块主建
     * @var type 
     */
    protected static $_primaryColumn  = 'id';

    //query日志
    protected static $_queryLog = array();
    protected static $_cache    = array();
    //表名
    protected $_tableName;
    protected $_alias           = null;
    protected $_tablePre;
    protected $_select          = null;
    protected $_distinct        = false;
    protected $_from            = null;
    protected $_limit           = null;
    protected $_offset          = null;
    protected $_where           = array();
    protected $_columns         = array();
    protected $_join            = array();
    protected $_columnsDefault  = array('*');
    protected $_values          = array();
    protected $_orderBy         = array();
    protected $_groupBy         = array();
    protected $_data            = array();
    protected $_isNew           = false;
    protected $_resetSelect     = true;


    private static $_query      = null;
    private static $_lastQuery  = null;
    

    public function  __construct($config=array()) {
        self::$_config = array(self::$_config,$config);
        self::$_db = Bic_Db_Driver::factory('mysqli');
        $this->_setupIdentifierQuoteCharacter();
        $this->_tablePre = !empty($this->_tablePre)?$this->_tablePre:TABLE_PREFIX;
    }
    
    /**
     *设置字段连接符 
     * 也可以通过self::config['identifier_quote_character']='`';进行设置
     */
    public function _setupIdentifierQuoteCharacter(){
        if (empty(self::$_config['identifier_quote_character'])){
            self::$_config['identifier_quote_character'] = $this->_detectIdentifierQuoteCharacter();
        }
    }
    
    /**
     *自动检测字段连接符
     * @return string 
     */
    public function _detectIdentifierQuoteCharacter(){
        switch (Bic_Db_Driver::getDriverInfo(self::$_db->get('_instanceKey'), 'driver')) {
            case 'pgsql':
            case 'sqlsrv':
            case 'dblib':
            case 'mssql':
            case 'sybase':
                return '"';
                break;
            case 'mysql':
            case 'sqlite':
            case 'sqlite2':
            default :
                return '`';
        }
    }
    /**
     *添加mysql字段专用引号
     * 例如：`column`
     * @param type $columnName
     * @return type 
     */
    protected function _quoteIdentifier($columnName){
        $columnNamePart = explode('.', $columnName);
        return join('.',array_map(array($this,'_quoteIdentifierPart'), $columnNamePart));
    }
    /**
     *给表及字段部分添加符号
     * @param type $part
     * @return type 
     */
    protected function _quoteIdentifierPart($part){
        if($part == '*'){
            return $part;
        }
        if(isset(self::$_config['identifier_quote_character']) && $part!='')
            return self::$_config['identifier_quote_character'].$part.self::$_config['identifier_quote_character'];
        return $part;
    }
    /**
     *值添加符号
     * @param type $value
     * @return type 
     */
    protected function _quoteIdentifierValue($value){
        if (is_numeric($value) && strpos($value,'.') === false)
            return $value;
        return "'{$value}'";
    }
    /**
     *添加点位符
     * @param type $placeholdersTotal
     * @return type 
     */
    protected function _createPlaceholders($placeholdersTotal){
        return join(',',array_fill(0,$placeholdersTotal,'?'));
    }
    
    public function hasColumn($field,$prototype){
        $column['field'] = $field;
        $column['TYPE'] = $prototype['type']!=''?$prototype['type']:'varchar(200)';
        $column['CHARACTER SET'] = $prototype['character_set']!=''?$prototype['character_set']:defined(DB_CHARACTER_SET)?DB_CHARACTER_SET:'utf-8';
        $column['DEFAULT'] = $prototype['default']!=''?$prototype['default']:'NOT NULL';
        $column['COMMENT'] = $prototype['comment']!=''?$prototype['comment']:'';
        self::$_columns[] = $column;
        $this -> {$column['field']} = '';
    }
    
    /**
     *表别名
     * @param type $alias
     * @return \Bic_Model_Abstract 
     */
    public function tableAlias($alias){
        $this->_alias = $alias;
        return $this;
    }
    
    protected function _addResultColumn($field, $alias=null){
        
        if (!empty($field)){
            
            if(!is_null($alias) && ''!=$alias){
                $field .= " AS ".$this->_quoteIdentifier($alias);
            }

            $this->_columns[] = $field;
        }
        return $this;
    }
    
    /**
     *添加select字段
     * @param mixed $field
     * @param type $alias
     * @return \Bic_Model_Abstract 
     */
    public function select($field,$alias=null){
        //重置select条件
        if ($this -> _resetSelect){
            $this->_resetSelect();
        }
        
        if (!is_array($field)){
            $field = explode(',',$field);
        }
        $field = $this->_joinNotEmpty(', ', array_map(array($this,'_quoteIdentifier'),$field));
        
        return $this->_addResultColumn($field, $alias);
    }
    /**
     *添加不带绰号的select
     * @param type $field
     * @param type $alias
     * @return \Bic_Model_Abstract 
     */
    public function selectExpr($field,$alias=null){
        //重置select条件
        if ($this -> _resetSelect){
            $this->_resetSelect();
        }
        
        if (is_array($field)){
            $field = $this->_joinNotEmpty(', ', $field);
        }
        
        return $this->_addResultColumn($field, $alias);
    }
    /**
     *添加带函数的select字段
     * 些方法可理解为select的子方法，也就是不会重置select条件
     * 注：此方法不能同时输入多个字段，只支持单个字段
     * @param type $operator
     * @param type $field
     * @param type $alias
     * @return type 
     */
    public function selectMethod($operator, $field, $alias=null){
        if (!is_array($field)){
            $field = explode(',',$field);
        }
        
        $field = $operator.'('.join('.',array_map(array($this,'_quoteIdentifier'), $field)).')';
        
        return $this->_addResultColumn($field, $alias);
    }
    protected function setDefaultColumns(){
        $this->_columnsDefault = $this->_columns;
        return $this;
    }
    public function from(){}
    /**
     *添加where条件
     * @param type $columnName
     * @param type $value
     * @return \Bic_Model_Abstract 
     */
    public function where($columnName,$value){
        return $this->_addSimpleWhere($columnName,'=',$value);
    }
    public function whereRaw($columnName,array $value){
        return $this->_addWhere($columnName, $value);
    }
    /**
     *添加原生条件
     * @param type $columnName
     * @param type $value
     * @return \Bic_Model_Abstract 
     */
    protected function _addWhere($columnName,$value){
        if (!is_array($value)){
            $value = array($value);
        }
        $value = array_map(array($this,'_quoteIdentifierValue'),$value);
        $columnName = $columnName;
        $this -> _where[] = array(
            self::columnFragmentIndex    => $columnName,
            self::columnValueIndex  => $value);
        return $this;
        
    }
    protected function _addSimpleWhere($columnName,$separator,$value){
        $columnName = $this->_quoteIdentifier($columnName);
        return $this->_addWhere("{$columnName} {$separator} ?", $value);
    }
    
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereEqual($columnName,$value){
        return $this->_addSimpleWhere($columnName,'=',$value);
    }
    /**
     *whereEqual匿名函数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereEq($columnName,$value){
        return $this->_addSimpleWhere($columnName,'=',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereNotEqual($columnName,$value){
        return $this->_addSimpleWhere($columnName,'!=',$value);
    }
    public function whereNotEq($columnName,$value){
        return $this->_addSimpleWhere($columnName,'!=',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereLt($columnName,$value){
        return $this->_addSimpleWhere($columnName,'<',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereGt($columnName,$value){
        return $this->_addSimpleWhere($columnName,'>',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereLte($columnName,$value){
        return $this->_addSimpleWhere($columnName,'<=',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereGte($columnName,$value){
        return $this->_addSimpleWhere($columnName,'>=',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereIn($columnName,$value){
        $valueArr = is_array($value)?$value:explode(',',$value);
        $columnName = $this->_quoteIdentifier($columnName);
        $placeholders = $this->_createPlaceholders(count($valueArr));
        return $this->_addWhere("{$columnName} IN ({$placeholders})",$valueArr);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereNotIn($columnName,$value){
        $valueArr = is_array($value)?$value:explode(',',$value);
        $columnName = $this->_quoteIdentifier($columnName);
        $placeholders = $this->_createPlaceholders(count($valueArr));
        return $this->_addWhere("{$columnName} NOT IN ({$placeholders})",$valueArr);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereLike($columnName,$value){
        return $this->_addSimpleWhere($columnName,'LIKE',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function whereNotLike($columnName,$value){
        return $this->_addSimpleWhere($columnName,'NOT LIKE',$value);
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type     
     */
    public function whereNll($columnName){
        return $this->_addWhere("{$columnName} IS NULL");
    }
    /**
     *添加where参数
     * @param type $columnName
     * @param type $value
     * @return type 
     */
    public function notwhereNotNll($columnName=null){
        return $this->_addWhere("{$columnName} IS NOT NULL");
    }
    public function limit($limit=null){
        $this->_limit = $limit;
        return $this;
    }
    public function offset($offset=null){
        $this->_offset = $offset;
        return $this;
    }
    public function groupBy($columnName){
        if (!in_array($columnName,$this->_groupBy)){
            $this->_groupBy[] = $columnName;
        }
        return $this;
    }
    public function order($orderBy){
        $this->_orderBy[] = "$orderBy";
        return $this;
    }
    public function orderBy($columnName,$order='ASC'){
        $this->_orderBy[] = "{$columnName} {$order}";
        return $this;
    }
    public function orderByAsc($columnName){
        $this->orderBy($columnName,'ASC');
        return $this;
    }
    public function orderByDesc($columnName){
        $this->orderBy($columnName,'DESC');
        return $this;
    }
    /**
     *添加join连接查询
     * @param type $joinOprator
     * @param type $table
     * @param type $joinOnCondition
     * @param type $tableAlias
     * @return \Bic_Model_Abstract 
     */
    protected function _addJoin($joinOprator, $table, $joinOnCondition, $tableAlias=null){
        
        $table = $this->_quoteIdentifier($this->_tablePre.$table);
        
        //添加表别名
        if (!is_null($tableAlias) && !empty($tableAlias)){
            $tableAlias = $this->_quoteIdentifier($tableAlias);
            $table .= " {$tableAlias}";
        }
        
        //生成join on 后面的限制条件
        if (is_array($joinOnCondition)){
            list($column, $operator, $column2) = $joinOnCondition;
            $column = $this->_quoteIdentifier($column);
            $column2 = $this->_quoteIdentifier($column2);
            $joinOnCondition = "{$column} {$operator} {$column2}";
        }
        
        $this->_join[] = "{$joinOprator} JOIN {$table} ON {$joinOnCondition}";
        return $this;
    }
    public function leftJoin($table, $condition, $alias){
        return $this->_addJoin('LEFT', $table, $condition, $alias);
    }

    /**
     *创建select sql语句开始部分
     * @return string 
     */
    protected function _buildSelectStart(){
        $columns = $this->_joinNotEmpty(', ', $this->_columns);
        if (''==$columns){
            $columns = $this->_joinNotEmpty(', ', $this->_columnsDefault);
        }
        if ($this->_distinct){
            $columns = "DISTINCT {$columns}";
        }
        $fragment = "SELECT {$columns} FROM ".$this->_quoteIdentifier(($this->_tablePre!=''?$this->_tablePre:TABLE_PREFIX).$this->_tableName);
        if (!is_null($this->_alias)){
            $fragment .= " ".$this->_quoteIdentifier($this->_alias);
        }
        return $fragment;
    }
    
    protected function _buildJoin(){
        if (empty($this->_join)){
            return ;
        }
        return $this->_joinNotEmpty(' ', $this->_join);
    }

    /**
     *创建where部分
     * @return type 
     */
    protected function _buildWhere(){
        if (count($this->_where)<1)
            return '';
        $conditions = array();
        //初始化value
        $this->_values = array();
        foreach($this->_where as $val){
            $conditions[] = $val[self::columnFragmentIndex];
            $this->_values = array_merge($this->_values,$val[self::columnValueIndex]);
        }
        return 'WHERE '.join(' AND ', $conditions);
    }
    /**
     *创建limit
     * @return string 
     */
    protected function _buildLimit(){
        if (empty($this->_limit))
            return '';
        $fragment = 'LIMIT '.$this->_buildOffset().$this->_limit;
        return $fragment;
    }
    /**
     *创建offset
     * @return string 
     */
    protected function _buildOffset(){
        if (empty($this->_offset)){
            return ;
        }
        return $this->_offset.',';
    }
    protected function _buildGroupBy(){
        if (count($this->_groupBy)<1)
            return '';
        return 'GROUP BY '.$this->_joinNotEmpty(',', $this->_groupBy);
    }
    protected function _buildOrderBy(){
        if (count($this->_orderBy)<1)
            return '';
        return 'ORDER BY '.$this->_joinNotEmpty(',', $this->_orderBy);
    }

    /**
     * 创建Select查询语句
     */
    protected function _buildSelect() {

        self::$_query = join(' ', array(
                            $this->_buildSelectStart(),
                            $this->_buildJoin(),
                            $this->_buildWhere(),
                            $this->_buildGroupBy(),
                            $this->_buildOrderBy(),
                            $this->_buildLimit()
                        )
        );
        return self::$_query;
    }
    
    /**
     *创建保存对象
     * @param type $primaryKey 
     */
    public function create($primaryKey){
        $rows = array();
        $this -> _isNew = true;
        $this -> _data = null;
        if (!empty($primaryKey)){
            $rows = $this->select();
            $primaryKey = is_array($primaryKey)?$primaryKey:explode(',',$primaryKey);
            $rows = $rows->whereIn(self::$_primaryColumn,$primaryKey);
            $rows = $rows->debug()->one();
            $this -> _isNew = false;
        }
        $this->_data = $rows;
        return $this;
    }
    /**
     *修改当前状态，可在select或selectExpr时，重不重置搜索条件。
     * 主要用在count()方法中：当使用count()方法以后，将会清空当前sql条件，
     * 使用setResetSelect(false)时，count()中的清空操作将不生效
     * 注：当前状态，只会生效一次！也就是说使用完以后，_resetSelect的状态将会恢复到true
     * @param type $resetSelectStatue
     * @return \Bic_Model_Abstract 
     */
    public function setResetSelect($resetSelectStatue=false){
        $this -> _resetSelect = $resetSelectStatue;
        return $this;
    }
    protected function _resetSelect(){
        $this->_select      = array();
        $this->_distinct    = false;
        $this->_from        = array();
        $this->_columns     = array();
        $this->_groupBy     = array();
        $this->_limit       = null;
        $this->_offset      = null;
        $this->_orderBy     = array();
        $this->_where       = array();
        $this->_values      = array();
    }
    protected function _parseQuery($query,$parameters){
        if (count($parameters)>0){
            $query = str_replace('?', "%s", $query);
            $query = vsprintf($query, $parameters);
        }
        self::$_lastQuery = $query;
        return $query;
    }
    /**
     *执行select
     * @return type 
     */
    protected function _execute(){
        $query = $this->_buildSelect();
        
        if (self::config('cache')){
            $cacheKey = self::_createCacheKey($query, $this->_values);
            $cacheContent = self::_checkCache($cacheKey);
            
            if($cacheContent !== false){
                return $cacheContent;
            }
        }
        
        $query = $this->_parseQuery($query, $this->_values);
        $result = self::$_db->query($query);
        
        if (self::config('cache')){
            self::_cacheQueryResult($cacheKey, $result);
        }
        
        $this->_buildDebugInfo($result);
        
        return $result;
    }
    /**
     *生成缓存索引
     * @param type $query
     * @param type $values
     * @return type 
     */
    protected static function _createCacheKey($query,$values){
        $query=empty($query)?self::$_query:$query;
        $values=empty($values)?$this->_values:$values;
        return $query.':'.$values;
    }
    /**
     *检测是否已缓存
     * @param type $key
     * @return boolean | query result
     */
    protected static function _checkCache($key){
        if (isset(self::$_cache[$key])){
            return self::$_cache[$key];
        }
        return false;
    }
    /**
     *生成缓存
     * @param type $key
     * @param type $cacheContent 
     */
    protected static function _cacheQueryResult($key,$cacheContent){
        self::$_cache[$key] = $cacheContent;
    }

    /**
     *计算总数
     * @return type 
     */
    public function count() {
        
        $queryFragment = join(' ', array(
                            $this->_buildJoin(),
                            $this->_buildWhere(),
                            $this->_buildGroupBy(),
                            $this->_buildOrderBy()
                        )
        );
        $query = join(' ', array(
                "SELECT ". ( is_null($this->_alias)?'':$this->_quoteIdentifier($this->_alias).'.' ) . self::$_primaryColumn ." FROM ",
                $this->_quoteIdentifier(($this->_tablePre!=''?$this->_tablePre:TABLE_PREFIX).$this->_tableName),
                $this->_quoteIdentifier($this->_alias),
                $queryFragment
            )
        );
        $query = $this->_parseQuery($query, $this->_values);
        $count = count(self::$_db->query($query));
        $this -> _resetSelect = true;
        
        $this->_buildDebugInfo("查询的结果总数为:{$count}");
        return $count;
    }
    public function setTable(){}
    public function getTable(){}
    public function getTableName(){
        return self::$_tabble;
    }
    public function query(){}
    
    public function add(){}
    
    /**
     *保存数据
     * 只能保存单条数据
     * @param array $data 
     */
    public function save(array $data){
        if (!empty($data)){
            $this -> _data = array_merge($this -> _data,$data);
        }
        if (empty($this->_data[self::$_primaryColumn])){
            self::$_query = $this -> _buildInsert();
        }else {
            self::$_query = $this -> _buildUpdate();
        }
        
        $query = $this->_parseQuery(self::$_query, array_map(array($this,'_quoteIdentifierValue'),$this->_data));

        if (self::$_config['debug']){
            $this->_buildDebugInfo('新增及更新debug，不执行代码，所以没有结果!<br/>请直接对上面的SQL进行人工判断');
            //保存数据中，
            //如果开启了debug，那么将不执行代码，
            //只是打印出代码
            return null;
        }
        self::$_db->justQuery($query);
        return self::$_db->insertId();
    }
    /**
     *添加数据
     * @return \Bic_Model_Abstract 
     */
    public function data(){
        $params1 = func_get_arg(0);
        if (func_num_args() >1 ){
            $params2 = func_get_arg(1);
            $this->_data[$params1] = $params2;
        }elseif (!is_array($params1)){
            $this->_data[$params1] = $params1;
        }else{
            $this->_data = array_merge($this->_data,$params1);
        }
        return $this;
    }

    protected function _buildUpdate(){
        //过滤要添加的值
        //将数据库中，没有的字段去除
        $this->_data = array_intersect_key($this->_data,array_combine($this->getFields(), $this->getFields()));
        $query = array();
        $querySetConditiong = array();
        $query[] = "UPDATE {$this->_quoteIdentifier($this->_tablePre.$this->_tableName)} SET ";
        $fields = array_map(array($this,'_quoteIdentifier'),array_keys($this->_data));
        foreach($fields as $val){
            $querySetConditiong[] = "{$val}=?";
        }
        $query[] = join(',',$querySetConditiong);
        $query[] = "WHERE {$this->_quoteIdentifier(self::$_primaryColumn)}={$this->_data[self::$_primaryColumn]}";
        return join(" ",$query);
    }
    protected function _buildInsert(){
        //过滤要添加的值
        //将数据库中，没有的字段去除
        $this->_data = array_intersect_key($this->_data,array_combine($this->getFields(), $this->getFields()));
        $query = array();
        $query[] = "INSERT INTO {$this->_quoteIdentifier($this->_tablePre.$this->_tableName)} ";
        $query[] = "(".join(',',array_map(array($this,'_quoteIdentifier'),array_keys($this->_data))).")";
        $query[] = "VALUES (".$this->_createPlaceholders(count($this->_data)).")";
        return join(" ",$query);
    }
    /**
     *获取当前表的字段信息
     * 并初始化$_fields
     * @return type 
     */
    public function getFields($table){
        if (!empty($this->_fields)){
            return $this->_fields;
        }
        $columns = self::$_db->getColumns($this->_tablePre.($table?$table:$this->_tableName));
        foreach ($columns as $col_val){
            $this->_fields[] = $col_val['Field'];
        }
        return $this->_fields;
    }
    public function delete(){}
    public function find($primary){}
    public function one(){
        $this->limit(1);
        $result = $this->_execute();
        return $result[0];
    }
    public function all(){
        $rows = $this->_execute();
        return $rows;
    }
    
    public function page($pageConfig){
        $_GET[$pageConfig['page_name']] || $_GET[$pageConfig['page_name']]=1;
        $pageConfig['page'] = $_GET[$pageConfig['page_name']];
        
        $pageConfig['total'] = $this->setResetSelect(false)->debug(false)->count();   //总数
        
        $data[$pageConfig['result']] = $this->limit($pageConfig['offset'])
                ->offset($pageConfig['offset']*($_GET[$pageConfig['page_name']]-1))
                ->debug($pageConfig['debug'])->all();
        
        $page = new Bic_View_Helper_Page($pageConfig);
        $data[$pageConfig['page_nav_name']] = $page->render(1);
        
        
        Bic_View::getInstance()->assign($pageConfig['result'],$data[$pageConfig['result']]);
        Bic_View::getInstance()->assign($pageConfig['page_nav_name'],$data[$pageConfig['page_nav_name']]);
        
        return $data;
    }
    protected function _joinNotEmpty($glue, $pieces){
        $pieces_new = array();
        foreach($pieces as $v){
            if(!empty($v)){
                $pieces_new[] = $v;
            }
        }
        return join($glue, $pieces_new);
    }
    protected static function config($key,$value=null){
        if (isset($value)){
            self::$_config[$key] = $value;
        }
        return isset(self::$_config[$key])?self::$_config[$key]:null;
    }
    public function debug($debug=true){
        self::config('debug', $debug);
        return $this;
    }
    protected function _buildDebugInfo($result){
        
        if (self::config('debug')){
            debug('<h1>Debug:</h1>');
            debug('<h3>The SQL of query:</h3>');
            debug(self::$_lastQuery);
            debug('<h3>The Log of query:</h3>');
            debug(self::$_db->error());
            debug('<h3>The ['.count($result).'] result of query:</h3>');
            debug($result);
            $this->debug(false);
        }
        return self::config('debug');
    }
    public function clean(){}
    
    protected function _set($key,$val){
        $this->_data[$key] = $val;
    }
    protected function _get($key){
        return isset($this->_data[$key])?$this->_data[$key]:null;
    }
    //魔术方法
    public function __set($key,$val){
        $this->_set($key,$val);
    }
    //魔术方法
    public function __get($key){
        return $this->_get($key,$val);
    }
}

?>